"""
Coding = utf-8
File:main.py
Author:刘永强
Date:2025/4/10 16:32
Software:PyCharm
Maintainer:依秋啊
Update Date:2025/5/7 16:52
"""

import hashlib
import io
from datetime import timedelta

import flask
import openpyxl
import pymysql.cursors
from flask import redirect, session, request, make_response
from flask_cors import CORS

import captcha
from api import bp
from utils import random_captcha_code, get_mysql_connection, check_login


app = flask.Flask(__name__)
CORS(app)
# 注册蓝图，app配置的路由与app上的路由混在一起
app.register_blueprint(bp)

# session对象可以存储请求信息，但必须设置一个密钥
# app.config['SECRET_KEY'] = 'Tl8sTUsNSzIjbjQkYDxGdmVoRmxHIWw8'
app.secret_key = 'Tl8sTUsNSzIjbjQkYDxGdmVoRmxHIWw8'
# 设置session过期时间seconds/days，用到时需开启
# app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
app.permanent_session_lifetime = timedelta(days=7)


"""
服务器创建session对象，记录用户相关数据，通过向浏览器写入cookie数据，让浏览器记录下会话的ID。
下次请求时，浏览器会在请求头中自动携带对应网站的cookie数据，而服务器可以从cookie中获取到sessionID，从而进一步获取session对象，再从session对象中获取用户相关数据，这样就实现了用户跟踪。
浏览器本地存储的方式:
- cookie(最普遍)
- localStorage / sessionStorage
- IndexedDB
"""


"""
# 该装饰器表示，请求路由到函数(show_index)之前检查
@app.before_request
def check_login():
    # request对象获取请求的路径
    url = request.path
    # session对象获取用户ID
    user_id = session.get('user_id')
    # 判断请求的路径是根目录(首页)并且获取不到用户ID，重定向到登录页
    if url == '/' and user_id is None:
        return redirect('/static/lyear_pages_login.html')
# 注意：该方式不够好，后面用装饰器实现用户登录检查
"""


@app.route('/')
# 用装饰器实现用户登录检查
@check_login
def show_index():
    return redirect('/static/index.html')


# 生成验证码
@app.route('/captcha')
def get_captcha_image():
    # captcha模块封装好的，创建验证码对象
    cap = captcha.Captcha.instance()  # type: captcha.Captcha
    # utils模块封装好的，生成随机验证码
    captcha_code = random_captcha_code()
    # session对象存储验证码
    session['captcha_code'] = captcha_code.lower()
    # 返回验证码二进制数据
    cap_image_data = cap.generate(captcha_code)
    # 构造响应对象
    resp = make_response(cap_image_data)
    # 指定响应头，告知浏览器响应头为图片
    resp.headers['content-type'] = 'image/png'
    return resp


# 实现用户登录
# methods表示只支持post请求，否则会报405错误
@app.route('/login', methods=['POST', ])
def login():
    # 1.验证码验证
    # 从前端消息体中获取json数据
    params = request.json
    # 前端用户从参数中发过来的验证码变小写
    captcha_from_user = params.get('captcha').lower()
    # 从session对象中获取的验证码
    captcha_from_sess = session.get('captcha_code')
    # 判断输入的验证码与session对象中保存的验证码是否一致
    if captcha_from_sess != captcha_from_user:
        return {'code': 10001, 'message': '验证码错误'}

    # 2.数据库(用户名，密码)验证
    # 从前端消息体中获取用户名，密码
    username = params.get('username')
    password = params.get('password')
    # 将用户输入的密码转换成md5
    password = hashlib.md5(password.encode()).hexdigest()
    conn = get_mysql_connection()
    try:
        with conn.cursor(pymysql.cursors.DictCursor) as cursor:
            cursor.execute(
                'select user_id, nickname, avatar from tb_user where username=%s and userpass=%s',
                (username, password)
            )
            user_dict = cursor.fetchone()
    finally:
        conn.close()
    # 一行数据都没有抓取到
    if user_dict is None:
        return {'code': 10002, 'message': '用户名或密码错误'}
    # 抓取到数据，在session会话中存入用户id
    session['user_id'] = user_dict['user_id']
    # 开启session过期时间，app对象需先设定过期时间
    session.permanent = True
    # 记录好用户的昵称和头像用于渲染(页面显示)
    nickname, avatar = user_dict['nickname'], user_dict['avatar']
    return {'code': 10000, 'message': '登录成功', 'nickname': nickname, 'avatar': avatar}


# 实现用户退出
@app.route('/logout')
# 用装饰器实现用户登录检查
@check_login
def logout():
    # 退出登录，删除session对象中记录的user_id
    session.pop('user_id')
    return {'code': 10003, 'message': '退出登录'}


# 导出数据为Excel表
@app.route('/export')
@check_login
def export_excel():
    wb = openpyxl.Workbook()
    sheet = wb.active
    sheet.append(('交易日', '开盘价', '收盘价', '最低价', '最高价', '交易量'))
    conn = get_mysql_connection(database='stock')
    try:
        with conn.cursor() as cursor:
            cursor.execute(
                'select trade_date, open_price, close_price, '
                'low_price, high_price, trade_volume '
                'from tb_baba_stock order by stock_id'
            )
            for row in cursor.fetchall():
                sheet.append(row)
    finally:
        conn.close()
    buffer = io.BytesIO()
    wb.save(buffer)
    resp = make_response(buffer.getvalue())
    resp.headers['content-type'] = 'application/vnd.ms-excel'
    resp.headers['content-disposition'] = f'attachment; filename="stocks.xlsx"'
    return resp


# 错误处理器
@app.errorhandler(404)
# error传递错误对象，记录错误消息
def show_error_page(error):
    return redirect('/static/lyear_pages_error.html')


if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=True)
